using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Nop.Admin.Models.Catalog;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Stores;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.ExportImport;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Media;
using Nop.Services.Security;
using Nop.Services.Seo;
using Nop.Services.Stores;
using Nop.Services.Tax;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc;
using Telerik.Web.Mvc;
using WebGrease.Css.Extensions;
using Nop.Core.Domain.Media;
using Nop.Services.Topics;

namespace Nop.Admin.Controllers
{
    [AdminAuthorize]
    public partial class ProductController : BaseNopController
    {
        #region Fields

        private readonly IProductService _productService;
        private readonly IProductTemplateService _productTemplateService;
        private readonly ICategoryService _categoryService;
        private readonly IManufacturerService _manufacturerService;
        private readonly ICustomerService _customerService;
        private readonly IUrlRecordService _urlRecordService;
        private readonly IWorkContext _workContext;
        private readonly ILanguageService _languageService;
        private readonly ILocalizationService _localizationService;
        private readonly ILocalizedEntityService _localizedEntityService;
        private readonly ISpecificationAttributeService _specificationAttributeService;
        private readonly IPictureService _pictureService;
        private readonly ITaxCategoryService _taxCategoryService;
        private readonly IProductTagService _productTagService;
        private readonly ICopyProductService _copyProductService;
        private readonly IPdfService _pdfService;
        private readonly IExportManager _exportManager;
        private readonly IImportManager _importManager;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly IPermissionService _permissionService;
        private readonly IAclService _aclService;
        private readonly ICurrencyService _currencyService;
        private readonly IStoreService _storeService;
        private readonly IRegionService _regionService;
        private readonly ITopicService _topicService;
        private readonly ILogger _logger;

        private readonly CurrencySettings _currencySettings;
        private readonly IMeasureService _measureService;
        private readonly MeasureSettings _measureSettings;
        private readonly PdfSettings _pdfSettings;
        private readonly AdminAreaSettings _adminAreaSettings;
        private readonly MediaSettings _mediaSettings;

        #endregion

        #region Constructors

        public ProductController(IProductService productService,
            IProductTemplateService productTemplateService,
            ICategoryService categoryService, IManufacturerService manufacturerService,
            ICustomerService customerService,
            IUrlRecordService urlRecordService, IWorkContext workContext, ILanguageService languageService,
            ILocalizationService localizationService, ILocalizedEntityService localizedEntityService,
            ISpecificationAttributeService specificationAttributeService, IPictureService pictureService,
            ITaxCategoryService taxCategoryService, IProductTagService productTagService,
            ICopyProductService copyProductService, IPdfService pdfService,
            IExportManager exportManager, IImportManager importManager,
            ICustomerActivityService customerActivityService,
            IPermissionService permissionService, IAclService aclService,
            IStoreService storeService,
            ICurrencyService currencyService, IRegionService regionService,
            ILogger logger,CurrencySettings currencySettings,
            IMeasureService measureService, ITopicService topicService, MeasureSettings measureSettings,
            PdfSettings pdfSettings, AdminAreaSettings adminAreaSettings, MediaSettings mediaSettings)
        {
            this._productService = productService;
            this._productTemplateService = productTemplateService;
            this._categoryService = categoryService;
            this._manufacturerService = manufacturerService;
            this._customerService = customerService;
            this._urlRecordService = urlRecordService;
            this._workContext = workContext;
            this._languageService = languageService;
            this._localizationService = localizationService;
            this._localizedEntityService = localizedEntityService;
            this._specificationAttributeService = specificationAttributeService;
            this._pictureService = pictureService;
            this._taxCategoryService = taxCategoryService;
            this._productTagService = productTagService;
            this._copyProductService = copyProductService;
            this._pdfService = pdfService;
            this._exportManager = exportManager;
            this._importManager = importManager;
            this._customerActivityService = customerActivityService;
            this._permissionService = permissionService;
            this._aclService = aclService;
            this._currencyService = currencyService;
            this._currencySettings = currencySettings;
            this._measureService = measureService;
            this._regionService = regionService;
            this._logger = logger;
            this._measureSettings = measureSettings;
            this._pdfSettings = pdfSettings;
            this._adminAreaSettings = adminAreaSettings;
            this._storeService = storeService;
            this._topicService = topicService;
            this._mediaSettings = mediaSettings;
        }

        #endregion

        #region Utitilies

        [NonAction]
        private void UpdateLocales(Product product, ProductModel model)
        {
            foreach (var localized in model.Locales)
            {
                _localizedEntityService.SaveLocalizedValue(product,
                                                               x => x.Name,
                                                               localized.Name,
                                                               localized.LanguageId);
                _localizedEntityService.SaveLocalizedValue(product,
                                                               x => x.ShortDescription,
                                                               localized.ShortDescription,
                                                               localized.LanguageId);
                _localizedEntityService.SaveLocalizedValue(product,
                                                               x => x.FullDescription,
                                                               localized.FullDescription,
                                                               localized.LanguageId);
                _localizedEntityService.SaveLocalizedValue(product,
                                                               x => x.MetaKeywords,
                                                               localized.MetaKeywords,
                                                               localized.LanguageId);
                _localizedEntityService.SaveLocalizedValue(product,
                                                               x => x.MetaDescription,
                                                               localized.MetaDescription,
                                                               localized.LanguageId);
                _localizedEntityService.SaveLocalizedValue(product,
                                                               x => x.MetaTitle,
                                                               localized.MetaTitle,
                                                               localized.LanguageId);

                //search engine name
                var seName = product.ValidateSeName(localized.SeName, localized.Name, false);
                _urlRecordService.SaveSlug(product, seName, localized.LanguageId);
            }
        }

        [NonAction]
        private void UpdatePictureSeoNames(Product product)
        {
            foreach (var pp in product.ProductPictures)
                _pictureService.SetSeoFilename(pp.PictureId, _pictureService.GetPictureSeName(product.Name));
        }

        [NonAction]
        protected void UpdateProductTagTotals(Product product)
        {
            var productTags = product.ProductTags;
            foreach (var productTag in productTags)
                _productTagService.UpdateProductTagTotals(productTag);
        }

        [NonAction]
        private void PrepareTemplatesModel(ProductModel model)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            var templates = _productTemplateService.GetAllProductTemplates();
            //foreach (var template in templates)
            //{
            //    model.AvailableProductTemplates.Add(new SelectListItem()
            //    {
            //        Text = template.Name,
            //        Value = template.Id.ToString()
            //    });
            //}
            //only need single variant
            model.ProductTemplateId = templates.Where(t => t.ViewPath == "ProductTemplate.SingleVariant").FirstOrDefault().Id;
        }

        [NonAction]
        private void PrepareAddSpecificationAttributeModel(ProductModel model)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            if (model.AddSpecificationAttributeModel == null)
                model.AddSpecificationAttributeModel = new ProductModel.AddProductSpecificationAttributeModel();

            //attributes
            var specificationAttributes = _specificationAttributeService.GetSpecificationAttributes();
            for (int i = 0; i < specificationAttributes.Count; i++)
            {
                var sa = specificationAttributes[i];
                model.AddSpecificationAttributeModel.AvailableAttributes.Add(new SelectListItem() { Text = sa.Name, Value = sa.Id.ToString() });
                if (i == 0)
                {
                    //attribute options
                    foreach (var sao in _specificationAttributeService.GetSpecificationAttributeOptionsBySpecificationAttribute(sa.Id))
                        model.AddSpecificationAttributeModel.AvailableOptions.Add(new SelectListItem() { Text = sao.Name, Value = sao.Id.ToString() });
                }
            }
        }

        [NonAction]
        private void PrepareAddProductPictureModel(ProductModel model)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            if (model.AddPictureModel == null)
                model.AddPictureModel = new ProductModel.ProductPictureModel();
        }

        [NonAction]
        private void PrepareCategoryMapping(ProductModel model)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            model.NumberOfAvailableCategories = _categoryService.GetAllCategories(showHidden: true).Count;
        }

        [NonAction]
        private void PrepareManufacturerMapping(ProductModel model)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            model.NumberOfAvailableManufacturers = _manufacturerService.GetAllManufacturers(true).Count;
        }

        [NonAction]
        private void PrepareAclModel(ProductModel model, Product product, bool excludeProperties)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            model.AvailableCustomerRoles = _customerService
                .GetAllCustomerRoles(true)
                .Select(cr => cr.ToModel())
                .ToList();
            if (!excludeProperties)
            {
                if (product != null)
                {
                    model.SelectedCustomerRoleIds = _aclService.GetCustomerRoleIdsWithAccess(product);
                }
                else
                {
                    model.SelectedCustomerRoleIds = new int[0];
                }
            }
        }

        [NonAction]
        protected void SaveProductAcl(Product product, ProductModel model)
        {
            var existingAclRecords = _aclService.GetAclRecords(product);
            var allCustomerRoles = _customerService.GetAllCustomerRoles(true);
            foreach (var customerRole in allCustomerRoles)
            {
                if (model.SelectedCustomerRoleIds != null && model.SelectedCustomerRoleIds.Contains(customerRole.Id))
                {
                    //new role
                    if (existingAclRecords.Where(acl => acl.CustomerRoleId == customerRole.Id).Count() == 0)
                        _aclService.InsertAclRecord(product, customerRole.Id);
                }
                else
                {
                    //removed role
                    var aclRecordToDelete = existingAclRecords.Where(acl => acl.CustomerRoleId == customerRole.Id).FirstOrDefault();
                    if (aclRecordToDelete != null)
                        _aclService.DeleteAclRecord(aclRecordToDelete);
                }
            }
        }

        [NonAction]
        private void PrepareVariantsModel(ProductModel model, Product product)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            if (product != null)
            {
                var variants = _productService.GetProductVariantsByProductId(product.Id, 0, true);
                foreach (var variant in variants)
                {
                    var variantModel = variant.ToModel();
                    variantModel.Region = _regionService.GetRegionById(variant.RegionId);
                    if (String.IsNullOrEmpty(variantModel.Name))
                        variantModel.Name = "Unnamed";
                    model.ProductVariantModels.Add(variantModel);
                }
            }
        }

        [NonAction]
        private void PrepareProductPictureThumbnailModel(ProductModel model, Product product)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            if (product != null)
            {
                if (_adminAreaSettings.DisplayProductPictures)
                {
                    var defaultProductPicture = _pictureService.GetPicturesByProductId(product.Id, 1).FirstOrDefault();
                    model.PictureThumbnailUrl = _pictureService.GetPictureUrl(defaultProductPicture, _mediaSettings.MiniCartThumbPictureSize, true);
                }
            }
        }

        [NonAction]
        private void PrepareCopyProductModel(ProductModel model, Product product)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            if (product != null)
            {
                model.CopyProductModel.Id = product.Id;
                model.CopyProductModel.Name = "Copy of " + product.Name;
                model.CopyProductModel.Published = true;
                model.CopyProductModel.CopyImages = true;
            }
        }

        [NonAction]
        private void PrepareTags(ProductModel model, Product product)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            if (product != null)
            {
                var result = new StringBuilder();
                for (int i = 0; i < product.ProductTags.Count; i++)
                {
                    var pt = product.ProductTags.ToList()[i];
                    result.Append(pt.Name);
                    if (i != product.ProductTags.Count - 1)
                        result.Append(", ");
                }
                model.ProductTags = result.ToString();
            }
        }

        [NonAction]
        private void PrepareProductTopic(ProductModel model)
        {
            model.AvailableTopics = _topicService.GetAllTopics()
                .Where(t => !t.SystemName.StartsWith("api", StringComparison.InvariantCultureIgnoreCase))
                .Select(t => new SelectListItem { Text = t.SystemName, Value = t.SystemName }).ToList();
        }

        [NonAction]
        private void UpdateLocales(ProductTag productTag, ProductTagModel model)
        {
            foreach (var localized in model.Locales)
            {
                _localizedEntityService.SaveLocalizedValue(productTag,
                                                               x => x.Name,
                                                               localized.Name,
                                                               localized.LanguageId);
            }
        }

        [NonAction]
        private void FirstVariant_UpdateLocales(ProductVariant variant, ProductVariantModel model)
        {
            foreach (var localized in model.Locales)
            {
                _localizedEntityService.SaveLocalizedValue(variant,
                                                               x => x.Name,
                                                               localized.Name,
                                                               localized.LanguageId);
                _localizedEntityService.SaveLocalizedValue(variant,
                                                               x => x.Description,
                                                               localized.Description,
                                                               localized.LanguageId);
            }
        }

        [NonAction]
        private void FirstVariant_PrepareProductVariantModel(ProductVariantModel model, ProductVariant variant, bool setPredefinedValues)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            //tax categories
            var taxCategories = _taxCategoryService.GetAllTaxCategories();
            model.AvailableTaxCategories.Add(new SelectListItem() { Text = "---", Value = "0" });
            foreach (var tc in taxCategories)
                model.AvailableTaxCategories.Add(new SelectListItem() { Text = tc.Name, Value = tc.Id.ToString(), Selected = variant != null && !setPredefinedValues && tc.Id == variant.TaxCategoryId });

            model.PrimaryStoreCurrencyCode = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId).CurrencyCode;
            model.BaseWeightIn = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).Name;
            model.BaseDimensionIn = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId).Name;

            _regionService.GetAllRegions().ForEach(r => model.AvailableRegions.Add(new SelectListItem() { Text = r.Name, Value = r.Id.ToString() }));

            if (setPredefinedValues)
            {
                model.MaximumCustomerEnteredPrice = 1000;
                model.MaxNumberOfDownloads = 10;
                model.RecurringCycleLength = 100;
                model.RecurringTotalCycles = 10;
                model.StockQuantity = 10000;
                model.NotifyAdminForQuantityBelow = 1;
                model.OrderMinimumQuantity = 1;
                model.OrderMaximumQuantity = 10000;
                model.DisplayOrder = 1;

                model.UnlimitedDownloads = true;
                model.IsShipEnabled = true;
                model.Published = true;
            }

            //little hack here in order to hide some of properties of the first product variant
            //we do it because they dublicate some properties of a product
            model.HideNameAndDescriptionProperties = true;
            model.HidePublishedProperty = true;
            model.HideDisplayOrderProperty = true;
        }

        [NonAction]
        private string[] ParseProductTags(string productTags)
        {
            var result = new List<string>();
            if (!String.IsNullOrWhiteSpace(productTags))
            {
                string[] values = productTags.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string val1 in values)
                    if (!String.IsNullOrEmpty(val1.Trim()))
                        result.Add(val1.Trim());
            }
            return result.ToArray();
        }

        [NonAction]
        private void SaveProductTags(Product product, string[] productTags)
        {
            if (product == null)
                throw new ArgumentNullException("product");

            //product tags
            var existingProductTags = product.ProductTags.OrderByDescending(pt => pt.ProductCount).ToList();
            var productTagsToDelete = new List<ProductTag>();
            foreach (var existingProductTag in existingProductTags)
            {
                bool found = false;
                foreach (string newProductTag in productTags)
                {
                    if (existingProductTag.Name.Equals(newProductTag, StringComparison.InvariantCultureIgnoreCase))
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    productTagsToDelete.Add(existingProductTag);
                }
            }
            foreach (var productTag in productTagsToDelete)
            {
                product.ProductTags.Remove(productTag);
                //ensure product is saved before updating totals
                _productService.UpdateProduct(product);
                _productTagService.UpdateProductTagTotals(productTag);
            }
            foreach (string productTagName in productTags)
            {
                ProductTag productTag = null;
                var productTag2 = _productTagService.GetProductTagByName(productTagName);
                if (productTag2 == null)
                {
                    //add new product tag
                    productTag = new ProductTag()
                    {
                        Name = productTagName,
                        ProductCount = 0
                    };
                    _productTagService.InsertProductTag(productTag);
                }
                else
                {
                    productTag = productTag2;
                }
                if (!product.ProductTagExists(productTag.Id))
                {
                    product.ProductTags.Add(productTag);
                    //ensure product is saved before updating totals
                    _productService.UpdateProduct(product);
                }
                //update product tag totals 
                _productTagService.UpdateProductTagTotals(productTag);
            }
        }
        #endregion

        #region Methods

        #region Product list / create / edit / delete

        //list products
        public ActionResult Index()
        {
            return RedirectToAction("List");
        }

        public ActionResult List()
        {
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            ViewBag.CanManageAll = canManageAllStore;
            ViewBag.AddNewProduct = isStoreAdmin;

            var model = new ProductListModel();
            model.DisplayProductPictures = _adminAreaSettings.DisplayProductPictures;
            model.DisplayPdfDownloadCatalog = _pdfSettings.Enabled;

            _regionService.GetAllRegions().ForEach(r=>model.AvailableRegions.Add(new SelectListItem(){ Text= r.Name, Value = r.Id.ToString()}));

            //categories
            model.AvailableCategories.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var c in _categoryService.GetAllCategories(showHidden: true))
                model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_categoryService), Value = c.Id.ToString() });

            //manufacturers
            model.AvailableManufacturers.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var m in _manufacturerService.GetAllManufacturers(true))
                model.AvailableManufacturers.Add(new SelectListItem() { Text = m.Name, Value = m.Id.ToString() });

            //stores
            model.SearchStoreId = curManagedStoreId;

            return View(model);
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult ProductList(GridCommand command, ProductListModel model)
        {
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return Content("Access Denied");

            var gridModel = new GridModel();

            if (isStoreAdmin)
            {
                if (!canManageAllStore)
                {
                    List<int> storeIds = new List<int>();
                    int adminCustomerId = curManagedStoreId == Store.AllStoreReservedId ? 0 : _workContext.CurrentCustomer.Id;
                    var stores = _storeService.GetAllStores(true, false, adminCustomerId);
                    storeIds.AddRange(stores.Select(s => s.Id));
                    if (!storeIds.Contains(model.SearchStoreId)) //Validate if the customer can view products of the specified store
                    {
                        return Content("Acess Denied");
                    }
                }
            }

            IList<int> filterableSpecificationAttributeOptionIds = null;
            var products = _productService.SearchProducts(model.SearchRegionId, model.SearchCategoryId,
                model.SearchManufacturerId, null, null, null, 0, model.SearchProductName, false, false,
                _workContext.WorkingLanguage.Id, new List<int>(),
                ProductSortingEnum.Position, command.Page - 1, command.PageSize,
                false, out filterableSpecificationAttributeOptionIds, true, model.SearchStoreId);

            gridModel.Data = products.Select(x =>
            {
                var productModel = x.ToModel();
                PrepareProductPictureThumbnailModel(productModel, x);
                return productModel;
            });
            gridModel.Total = products.TotalCount;
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [HttpPost, ActionName("List")]
        [FormValueRequired("go-to-product-by-sku")]
        public ActionResult GoToSku(ProductListModel model)
        {
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            string sku = model.GoDirectlyToSku;
            var pv = _productService.GetProductVariantBySku(sku);
            if (pv != null)
                return RedirectToAction("Edit", "ProductVariant", new { id = pv.Id });

            //not found
            return List();
        }

        //create product
        public ActionResult Create()
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore, isStoreAdmin, isAccessDenied;
            _permissionService.SecurityAccessForStoreAdmin(out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();
            var model = new ProductModel();

            //product
            AddLocales(_languageService, model.Locales);
            PrepareTemplatesModel(model);
            PrepareAddSpecificationAttributeModel(model);
            PrepareAddProductPictureModel(model);
            PrepareCategoryMapping(model);
            PrepareManufacturerMapping(model);
            PrepareProductTopic(model);

            //default values
            model.Published = true;
            model.AllowCustomerReviews = true;

            //only storeAdmin  or admin can create product
            var adminCustomerId = customer.Id;
            if (canManageAllStore)
            {
                model.IsAdmin = true;
                PrepareAclModel(model, null, false);
                adminCustomerId = 0;
            }
            var stores = _storeService.GetAllStores(false, false, adminCustomerId);
            model.StoreId = stores.FirstOrDefault().Id;
            model.Store = stores.FirstOrDefault().Name;
            model.IsCustomerStoreAdmin = true;

            //first product variant
            model.FirstProductVariantModel = new ProductVariantModel();
            AddLocales(_languageService, model.FirstProductVariantModel.Locales);
            FirstVariant_PrepareProductVariantModel(model.FirstProductVariantModel, null, true);
            model.FirstProductVariantModel.IsStoreAdmin = true;
            return View(model);
        }

        [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")]
        public ActionResult Create(ProductModel model, bool continueEditing)
        {
            bool canManageAllStore, isStoreAdmin, isAccessDenied;
            _permissionService.SecurityAccessForStoreAdmin(out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();
            //TODO: verify store exists
            var store = _storeService.GetStoreById(model.StoreId);
            if (store == null)
                throw new ArgumentNullException("store");
            var customer = _workContext.CurrentCustomer;
            if (!canManageAllStore && store.AdminCustomerId != customer.Id)
            {
                return AccessDeniedView();
            }

            if (ModelState.IsValid)
            {
                //product
                PrepareTemplatesModel(model);
                var product = model.ToEntity();
                product.CreatedOnUtc = DateTime.UtcNow;
                product.UpdatedOnUtc = DateTime.UtcNow;

                // Make MetaDescription is not empty to avoid social media picking up
                // garbage info when sharing a product to it.
                if (String.IsNullOrEmpty(product.MetaDescription))
                {
                    var defaultMetaDescription = product.Name;   // which cannot be empty!

                    if (!String.IsNullOrWhiteSpace(product.ShortDescription))
                    {
                        defaultMetaDescription += " -- " + product.ShortDescription;
                    }

                    product.MetaDescription = defaultMetaDescription;
                }

                product.Published = model.Published;
                product.AllowCustomerReviews = model.AllowCustomerReviews;
                product.StoreId = model.StoreId;
                product.AgeClass = model.AgeClass;
                if (canManageAllStore)
                {
                    product.ShowOnHomePage = model.ShowOnHomePage;
                    //ACL (customer roles)
                    SaveProductAcl(product, model);
                }
                _productService.InsertProduct(product);
                //search engine name
                model.SeName = product.ValidateSeName(model.SeName, product.Name, true);
                _urlRecordService.SaveSlug(product, model.SeName, 0);
                //locales
                UpdateLocales(product, model);
                //tags
                SaveProductTags(product, ParseProductTags(model.ProductTags));

                //default product variant
                var variant = model.FirstProductVariantModel.ToEntity();
                variant.ProductId = product.Id;
                variant.Published = true;
                variant.DisplayOrder = 1;
                variant.CreatedOnUtc = DateTime.UtcNow;
                variant.UpdatedOnUtc = DateTime.UtcNow;
                _productService.InsertProductVariant(variant);
                FirstVariant_UpdateLocales(variant, model.FirstProductVariantModel);

                //activity log
                _customerActivityService.InsertActivity("AddNewProduct", _localizationService.GetResource("ActivityLog.AddNewProduct"), product.Name);

                SuccessNotification(_localizationService.GetResource("Admin.Catalog.Products.Added"));
                return continueEditing ? RedirectToAction("Edit", new { id = product.Id }) : RedirectToAction("List");
            }

            //If we got this far, something failed, redisplay form

            //product
            PrepareTemplatesModel(model);
            PrepareAddSpecificationAttributeModel(model);
            PrepareAddProductPictureModel(model);
            PrepareCategoryMapping(model);
            PrepareManufacturerMapping(model);
            model.IsAdmin = false;
            if (canManageAllStore)
            {
                PrepareAclModel(model, null, true);
                model.IsAdmin = true;
            }
            model.IsCustomerStoreAdmin = true;
            //first product variant
            FirstVariant_PrepareProductVariantModel(model.FirstProductVariantModel, null, false);
            return View(model);
        }

        //edit product
        public ActionResult Edit(int id)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var product = _productService.GetProductById(id);
            if (product == null || product.Deleted)
                //No product found with the specified id
                return RedirectToAction("List");

            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return RedirectToAction("List");
            }
            if (!canManageAllStore)
            {
                if (store.AdminCustomerId != customer.Id && curManagedStoreId != product.StoreId)
                {
                    return AccessDeniedView();
                }
            }

            var model = product.ToModel();

            //store
            //TODO: do we need this unsecured function?
            //model.StoreId = curManagedStoreId;
            model.StoreId = product.StoreId;
            model.Store = store.Name;

            if (isStoreAdmin || canManageAllStore)
            {
                model.IsCustomerStoreAdmin = true;
                PrepareCopyProductModel(model, product);
                PrepareCategoryMapping(model);
            }
            else
            {
                model.IsCustomerStoreAdmin = false;
            }

            AddLocales(_languageService, model.Locales, (locale, languageId) =>
            {
                locale.Name = product.GetLocalized(x => x.Name, languageId, false, false);
                locale.ShortDescription = product.GetLocalized(x => x.ShortDescription, languageId, false, false);
                locale.FullDescription = product.GetLocalized(x => x.FullDescription, languageId, false, false);
                locale.MetaKeywords = product.GetLocalized(x => x.MetaKeywords, languageId, false, false);
                locale.MetaDescription = product.GetLocalized(x => x.MetaDescription, languageId, false, false);
                locale.MetaTitle = product.GetLocalized(x => x.MetaTitle, languageId, false, false);
                locale.SeName = product.GetSeName(languageId, false, false);
            });
            model.IsAdmin = false;
            if (canManageAllStore)
            {
                model.IsAdmin = true;
                PrepareAclModel(model, product, false);
            }
            PrepareTags(model, product);
            PrepareVariantsModel(model, product);
            PrepareTemplatesModel(model);
            PrepareAddSpecificationAttributeModel(model);
            PrepareAddProductPictureModel(model);
            PrepareManufacturerMapping(model);
            PrepareProductTopic(model);
            return View(model);
        }

        [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")]
        public ActionResult Edit(ProductModel model, bool continueEditing)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var product = _productService.GetProductById(model.Id);
            if (product == null || product.Deleted)
            {
                //No product found with the specified id
                ErrorNotification(_localizationService.GetResource("Admin.Product.Common.NoProduct"));
                return RedirectToAction("List");
            }

            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                ErrorNotification(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
                return RedirectToAction("List");
            }
            if (!canManageAllStore)
            {
                if (store.AdminCustomerId != customer.Id && curManagedStoreId != product.StoreId)
                {
                    return AccessDeniedView();
                }
            }

            if (ModelState.IsValid)
            {
                //product
                PrepareTemplatesModel(model);
                product = model.ToEntity(product);
                product.UpdatedOnUtc = DateTime.UtcNow;
                product.Store = store;
				product.AgeClass = model.AgeClass;
                // Make MetaDescription is not empty to avoid social media picking up
                // garbage info when sharing a product to it.
                // - At this point, user must have deleted the metadescription.
                if (String.IsNullOrEmpty(product.MetaDescription))
                {
                    product.MetaDescription = " ";  // just enter a white-space.
                }
                if (isStoreAdmin || canManageAllStore)
                {
                    if (canManageAllStore)
                    {
                        //Only change by admin
                        product.ShowOnHomePage = model.ShowOnHomePage;
                        //ACL (customer roles)
                        SaveProductAcl(product, model);
                    }
                    product.Published = model.Published;
                    product.AllowCustomerReviews = model.AllowCustomerReviews;
                    product.StoreId = model.StoreId;
                }

                _productService.UpdateProduct(product);
                //search engine name
                model.SeName = product.ValidateSeName(model.SeName, product.Name, true);
                _urlRecordService.SaveSlug(product, model.SeName, 0);
                //locales
                UpdateLocales(product, model);
                //tags
                SaveProductTags(product, ParseProductTags(model.ProductTags));
                //picture seo names
                UpdatePictureSeoNames(product);

                //activity log
                _customerActivityService.InsertActivity("EditProduct", _localizationService.GetResource("ActivityLog.EditProduct"), product.Name);

                SuccessNotification(_localizationService.GetResource("Admin.Catalog.Products.Updated"));

                //update homepageProductCache
                _productService.RemoveHomepageProductCache();

                return continueEditing ? RedirectToAction("Edit", new { id = product.Id }) : RedirectToAction("List");
            }

            //If we got this far, something failed, redisplay form
            PrepareTags(model, product);
            if (isStoreAdmin || canManageAllStore)
            {
                model.IsCustomerStoreAdmin = true;
                PrepareCopyProductModel(model, product);
                PrepareCategoryMapping(model);
            }
            else
            {
                model.IsCustomerStoreAdmin = false;
            }
            PrepareVariantsModel(model, product);
            PrepareTemplatesModel(model);
            PrepareAddSpecificationAttributeModel(model);
            PrepareAddProductPictureModel(model);
            PrepareManufacturerMapping(model);
            model.IsAdmin = false;
            if (canManageAllStore)
            {
                model.IsAdmin = true;
                PrepareAclModel(model, product, true);
            }
            return View(model);
        }

        //delete product
        [HttpPost]
        public ActionResult Delete(int id)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore, isStoreAdmin, isAccessDenied;
            _permissionService.SecurityAccessForStoreAdmin(out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var product = _productService.GetProductById(id);
            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return RedirectToAction("List");
            }
            if (!canManageAllStore && customer.Id != store.AdminCustomerId)
            {
                return AccessDeniedView();
            }

            _productService.DeleteProduct(product);
            //update product tag totals
            UpdateProductTagTotals(product);

            //activity log
            _customerActivityService.InsertActivity("DeleteProduct", _localizationService.GetResource("ActivityLog.DeleteProduct"), product.Name);

            SuccessNotification(_localizationService.GetResource("Admin.Catalog.Products.Deleted"));
            return RedirectToAction("List");
        }

        public ActionResult DeleteSelected(string selectedIds)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore, isStoreAdmin, isAccessDenied;
            _permissionService.SecurityAccessForStoreAdmin(out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var products = new List<Product>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                products.AddRange(_productService.GetProductsByIds(ids, 0));

                for (int i = 0; i < products.Count; i++)
                {
                    var product = products[i];
                    var store = _storeService.GetStoreById(product.StoreId);
                    if (store == null)
                    {
                        ErrorNotification(_localizationService.GetResource("Admin.Product.Common.StoreNotExist", product.Id));
                        break;
                    }
                    if (!canManageAllStore)
                    {
                        if (customer.Id != store.AdminCustomerId)
                        {
                            ErrorNotification(_localizationService.GetResource("Admin.Product.Common.ProductNoAuthority", product.Id));
                            return AccessDeniedView();
                        }
                    }
                    _productService.DeleteProduct(product);
                    //update product tag totals
                    UpdateProductTagTotals(product);
                }
            }

            return RedirectToAction("List");
        }


        [GridAction(EnableCustomBinding = true)]
        public ActionResult GetVariants(int productId)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return Content("Access Denied");

            var product = _productService.GetProductById(productId);
            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }
            if (!canManageAllStore)
            {
                if (store.AdminCustomerId != customer.Id && curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != product.StoreId)
                {
                    return Content("Access Denied");
                }
            }

            var variants = _productService.GetProductVariantsByProductId(productId, _workContext.CurrentCustomer.Region.Id, true);

            var gridModel = new GridModel<ProductVariantModel>()
            {
                Data = variants.Select(x =>
                {
                    var variantModel = x.ToModel();
                    if (String.IsNullOrEmpty(variantModel.Name))
                        variantModel.Name = "Unnamed";
                    return variantModel;
                }),
                Total = variants.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [HttpPost]
        public ActionResult CopyProduct(ProductModel model)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore, isStoreAdmin, isAccessDenied;
            _permissionService.SecurityAccessForStoreAdmin(out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var copyModel = model.CopyProductModel;
            var product = _productService.GetProductById(copyModel.Id);
            if (product == null)
                throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.NoProduct"));
            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return RedirectToAction("List");
            }
            if (!canManageAllStore)
            {
                if (customer.Id != store.AdminCustomerId)
                {
                    return AccessDeniedView();
                }
            }

            try
            {
                var newProduct = _copyProductService.CopyProduct(copyModel.Id, copyModel.Name, copyModel.Published, copyModel.CopyImages);
                SuccessNotification("The product is copied");
                return RedirectToAction("Edit", new { id = newProduct.Id });
            }
            catch (Exception exc)
            {
                ErrorNotification(exc.Message);
                return RedirectToAction("Edit", new { id = copyModel.Id });
            }
        }

        #endregion

        #region Product categories

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult ProductCategoryList(GridCommand command, int productId)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageCatalog);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            int storeId = _productService.GetProductById(productId).StoreId;
            if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != storeId)
            {
                return AccessDeniedView();
            }

            var productCategories = _categoryService.GetProductCategoriesByProductId(productId, true);
            var productCategoriesModel = productCategories
                .Select(x =>
                {
                    return new ProductModel.ProductCategoryModel()
                    {
                        Id = x.Id,
                        Category = _categoryService.GetCategoryById(x.CategoryId).GetCategoryBreadCrumb(_categoryService),
                        ProductId = x.ProductId,
                        CategoryId = x.CategoryId,
                        IsFeaturedProduct = x.IsFeaturedProduct,
                        DisplayOrder = x.DisplayOrder
                    };
                })
                .ToList();

            var model = new GridModel<ProductModel.ProductCategoryModel>
            {
                Data = productCategoriesModel,
                Total = productCategoriesModel.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductCategoryInsert(GridCommand command, ProductModel.ProductCategoryModel model)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageCatalog);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var productId = model.ProductId;
            int storeId = _productService.GetProductById(productId).StoreId;
            if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != storeId)
            {
                return AccessDeniedView();
            }

            var categoryId = Int32.Parse(model.Category); //use Category property (not CategoryId) because appropriate property is stored in it

            var existingProductCategories = _categoryService.GetProductCategoriesByCategoryId(categoryId, 0, int.MaxValue, true);
            if (existingProductCategories.FindProductCategory(productId, categoryId) == null)
            {
                var productCategory = new ProductCategory()
                {
                    ProductId = productId,
                    CategoryId = categoryId,
                    IsFeaturedProduct = model.IsFeaturedProduct,
                    DisplayOrder = model.DisplayOrder
                };
                _categoryService.InsertProductCategory(productCategory);
            }

            return ProductCategoryList(command, productId);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductCategoryUpdate(GridCommand command, ProductModel.ProductCategoryModel model)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageCatalog);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var productId = model.ProductId;
            int storeId = _productService.GetProductById(productId).StoreId;
            if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != storeId)
            {
                return AccessDeniedView();
            }

            var productCategory = _categoryService.GetProductCategoryById(model.Id);
            if (productCategory == null)
                throw new ArgumentException("No product category mapping found with the specified id");

            //use Category property (not CategoryId) because appropriate property is stored in it
            productCategory.CategoryId = Int32.Parse(model.Category);
            productCategory.IsFeaturedProduct = model.IsFeaturedProduct;
            productCategory.DisplayOrder = model.DisplayOrder;
            _categoryService.UpdateProductCategory(productCategory);

            return ProductCategoryList(command, productCategory.ProductId);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductCategoryDelete(int id, GridCommand command)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageCatalog);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var productCategory = _categoryService.GetProductCategoryById(id);
            if (productCategory == null)
                throw new ArgumentException("No product category mapping found with the specified id");
            var productId = productCategory.ProductId;
            int storeId = _productService.GetProductById(productId).StoreId;
            if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != storeId)
            {
                return AccessDeniedView();
            }

            _categoryService.DeleteProductCategory(productCategory);

            return ProductCategoryList(command, productId);
        }

        #endregion

        #region Product manufacturers

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult ProductManufacturerList(GridCommand command, int productId)
        {

            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageCatalog);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            int storeId = _productService.GetProductById(productId).StoreId;
            if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != storeId)
            {
                return AccessDeniedView();
            }

            var productManufacturers = _manufacturerService.GetProductManufacturersByProductId(productId, true);
            var productManufacturersModel = productManufacturers
                .Select(x =>
                {
                    return new ProductModel.ProductManufacturerModel()
                    {
                        Id = x.Id,
                        Manufacturer = _manufacturerService.GetManufacturerById(x.ManufacturerId).Name,
                        ProductId = x.ProductId,
                        ManufacturerId = x.ManufacturerId,
                        IsFeaturedProduct = x.IsFeaturedProduct,
                        DisplayOrder = x.DisplayOrder
                    };
                })
                .ToList();

            var model = new GridModel<ProductModel.ProductManufacturerModel>
            {
                Data = productManufacturersModel,
                Total = productManufacturersModel.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductManufacturerInsert(GridCommand command, ProductModel.ProductManufacturerModel model)
        {

            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageCatalog);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var productId = model.ProductId;
            int storeId = _productService.GetProductById(productId).StoreId;
            if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != storeId)
            {
                return AccessDeniedView();
            }

            var manufacturerId = Int32.Parse(model.Manufacturer); //use Manufacturer property (not ManufacturerId) because appropriate property is stored in it

            var existingProductmanufacturers = _manufacturerService.GetProductManufacturersByManufacturerId(manufacturerId, 0, int.MaxValue, true);
            if (existingProductmanufacturers.FindProductManufacturer(productId, manufacturerId) == null)
            {
                var productManufacturer = new ProductManufacturer()
                {
                    ProductId = productId,
                    ManufacturerId = manufacturerId,
                    IsFeaturedProduct = model.IsFeaturedProduct,
                    DisplayOrder = model.DisplayOrder
                };
                _manufacturerService.InsertProductManufacturer(productManufacturer);
            }

            return ProductManufacturerList(command, productId);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductManufacturerUpdate(GridCommand command, ProductModel.ProductManufacturerModel model)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageCatalog);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var productManufacturer = _manufacturerService.GetProductManufacturerById(model.Id);
            if (productManufacturer == null)
                throw new ArgumentException("No product manufacturer mapping found with the specified id");

            //use Manufacturer property (not ManufacturerId) because appropriate property is stored in it
            productManufacturer.ManufacturerId = Int32.Parse(model.Manufacturer);
            productManufacturer.IsFeaturedProduct = model.IsFeaturedProduct;
            productManufacturer.DisplayOrder = model.DisplayOrder;

            int storeId = _productService.GetProductById(productManufacturer.ProductId).StoreId;
            if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != storeId)
            {
                return AccessDeniedView();
            }

            _manufacturerService.UpdateProductManufacturer(productManufacturer);

            return ProductManufacturerList(command, productManufacturer.ProductId);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductManufacturerDelete(int id, GridCommand command)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageCatalog);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var productManufacturer = _manufacturerService.GetProductManufacturerById(id);
            if (productManufacturer == null)
                throw new ArgumentException("No product manufacturer mapping found with the specified id");

            var productId = productManufacturer.ProductId;
            int storeId = _productService.GetProductById(productId).StoreId;
            if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != storeId)
            {
                return AccessDeniedView();
            }

            _manufacturerService.DeleteProductManufacturer(productManufacturer);

            return ProductManufacturerList(command, productId);
        }

        #endregion

        #region Related products

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult RelatedProductList(GridCommand command, int productId)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return Content("Access Denied");

            int storeId = _productService.GetProductById(productId).StoreId;
            var store = _storeService.GetStoreById(storeId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            var relatedProducts = _productService.GetRelatedProductsByProductId1(productId, 0, true);
            var relatedProductsModel = relatedProducts
                .Select(x =>
                {
                    return new ProductModel.RelatedProductModel()
                    {
                        Id = x.Id,
                        ProductId1 = x.ProductId1,
                        ProductId2 = x.ProductId2,
                        Product2Name = _productService.GetProductById(x.ProductId2).Name,
                        DisplayOrder = x.DisplayOrder
                    };
                })
                .ToList();

            var model = new GridModel<ProductModel.RelatedProductModel>
            {
                Data = relatedProductsModel,
                Total = relatedProductsModel.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult RelatedProductUpdate(GridCommand command, ProductModel.RelatedProductModel model)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return Content("Access Denied");

            var relatedProduct = _productService.GetRelatedProductById(model.Id);
            if (relatedProduct == null)
                return Content("No related product found with the specified id");

            relatedProduct.DisplayOrder = model.DisplayOrder;

            int storeId = _productService.GetProductById(relatedProduct.ProductId1).StoreId;
            var store = _storeService.GetStoreById(storeId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            _productService.UpdateRelatedProduct(relatedProduct);

            return RelatedProductList(command, relatedProduct.ProductId1);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult RelatedProductDelete(int id, GridCommand command)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return Content("Access Denied");

            var relatedProduct = _productService.GetRelatedProductById(id);
            if (relatedProduct == null)
                return Content("No related product found with the specified id");

            var productId = relatedProduct.ProductId1;
            int storeId = _productService.GetProductById(productId).StoreId;
            var store = _storeService.GetStoreById(storeId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            _productService.DeleteRelatedProduct(relatedProduct);

            return RelatedProductList(command, productId);
        }

        public ActionResult RelatedProductAddPopup(int productId)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return AccessDeniedView();

            int storeId = _productService.GetProductById(productId).StoreId;
            var store = _storeService.GetStoreById(storeId);
            if (store == null)
            {
                throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            IList<int> filterableSpecificationAttributeOptionIds = null;
            var products = _productService.SearchProducts(0, 0, 0, null, null, null, 0, string.Empty, false, false,
                _workContext.WorkingLanguage.Id, new List<int>(),
                ProductSortingEnum.Position, 0, _adminAreaSettings.GridPageSize,
                false, out filterableSpecificationAttributeOptionIds, true, Store.AllStoreReservedId);

            var model = new ProductModel.AddRelatedProductModel();
            model.Products = new GridModel<ProductModel>
            {
                Data = products.Select(x => x.ToModel()),
                Total = products.TotalCount
            };
            //categories
            model.AvailableCategories.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var c in _categoryService.GetAllCategories(showHidden: true))
                model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_categoryService), Value = c.Id.ToString() });

            //manufacturers
            model.AvailableManufacturers.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var m in _manufacturerService.GetAllManufacturers(true))
                model.AvailableManufacturers.Add(new SelectListItem() { Text = m.Name, Value = m.Id.ToString() });

            return View(model);
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult RelatedProductAddPopupList(GridCommand command, ProductModel.AddRelatedProductModel model)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return Content("Access Denied");

            int storeId = _productService.GetProductById(model.ProductId).StoreId;
            var store = _storeService.GetStoreById(storeId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            var gridModel = new GridModel();
            IList<int> filterableSpecificationAttributeOptionIds = null;
            var products = _productService.SearchProducts(0, model.SearchCategoryId, model.SearchManufacturerId,
                null, null, null, 0, model.SearchProductName, false, false,
                _workContext.WorkingLanguage.Id, new List<int>(),
                ProductSortingEnum.Position, command.Page - 1, command.PageSize,
                false, out filterableSpecificationAttributeOptionIds, true, Store.AllStoreReservedId);
            gridModel.Data = products.Select(x => x.ToModel());
            gridModel.Total = products.TotalCount;
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [HttpPost]
        [FormValueRequired("save")]
        public ActionResult RelatedProductAddPopup(string btnId, string formId, ProductModel.AddRelatedProductModel model)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return Content("Access Denied");

            int storeId = _productService.GetProductById(model.ProductId).StoreId;
            var store = _storeService.GetStoreById(storeId);
            if (store == null)
            {
                throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            if (model.SelectedProductIds != null)
            {
                foreach (int id in model.SelectedProductIds)
                {
                    var product = _productService.GetProductById(id);
                    if (product != null)
                    {
                        var existingRelatedProducts = _productService.GetRelatedProductsByProductId1(model.ProductId);
                        if (existingRelatedProducts.FindRelatedProduct(model.ProductId, id) == null)
                        {
                            _productService.InsertRelatedProduct(
                                new RelatedProduct()
                                {
                                    ProductId1 = model.ProductId,
                                    ProductId2 = id,
                                    DisplayOrder = 1
                                });
                        }
                    }
                }
            }

            ViewBag.RefreshPage = true;
            ViewBag.btnId = btnId;
            ViewBag.formId = formId;
            model.Products = new GridModel<ProductModel>();
            return View(model);
        }

        #endregion

        #region Cross-sell products

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult CrossSellProductList(GridCommand command, int productId)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return Content("Access Denied");

            var product = _productService.GetProductById(productId);
            if (product == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.NoProduct"));
            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            var crossSellProducts = _productService.GetCrossSellProductsByProductId1(productId, 0, true);
            var crossSellProductsModel = crossSellProducts
                .Select(x =>
                {
                    return new ProductModel.CrossSellProductModel()
                    {
                        Id = x.Id,
                        ProductId1 = x.ProductId1,
                        ProductId2 = x.ProductId2,
                        Product2Name = _productService.GetProductById(x.ProductId2).Name,
                    };
                })
                .ToList();

            var model = new GridModel<ProductModel.CrossSellProductModel>
            {
                Data = crossSellProductsModel,
                Total = crossSellProductsModel.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult CrossSellProductDelete(int id, GridCommand command)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return Content("Access Denied");

            var crossSellProduct = _productService.GetCrossSellProductById(id);
            if (crossSellProduct == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.Cross-SellProductNotExist"));

            var productId = crossSellProduct.ProductId1;
            var product = _productService.GetProductById(productId);
            if (product == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.ProductNotExist"));
            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            _productService.DeleteCrossSellProduct(crossSellProduct);

            return CrossSellProductList(command, productId);
        }

        public ActionResult CrossSellProductAddPopup(int productId)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return AccessDeniedView();

            var currentProduct = _productService.GetProductById(productId);
            if (currentProduct == null)
                throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.ProductNotExist"));
            var store = _storeService.GetStoreById(currentProduct.StoreId);
            if (store == null)
            {
                throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            IList<int> filterableSpecificationAttributeOptionIds = null;
            var products = _productService.SearchProducts(0, 0, 0, null, null, null, 0, string.Empty, false, false,
                _workContext.WorkingLanguage.Id, new List<int>(),
                ProductSortingEnum.Position, 0, _adminAreaSettings.GridPageSize,
                false, out filterableSpecificationAttributeOptionIds, true, Store.AllStoreReservedId);

            var model = new ProductModel.AddCrossSellProductModel();
            model.Products = new GridModel<ProductModel>
            {
                Data = products.Select(x => x.ToModel()),
                Total = products.TotalCount
            };
            //categories
            model.AvailableCategories.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var c in _categoryService.GetAllCategories(showHidden: true))
                model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_categoryService), Value = c.Id.ToString() });

            //manufacturers
            model.AvailableManufacturers.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var m in _manufacturerService.GetAllManufacturers(true))
                model.AvailableManufacturers.Add(new SelectListItem() { Text = m.Name, Value = m.Id.ToString() });

            return View(model);
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult CrossSellProductAddPopupList(GridCommand command, ProductModel.AddCrossSellProductModel model)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return Content("Access Denied");

            var currentProduct = _productService.GetProductById(model.ProductId);
            if (currentProduct == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.NoProduct"));
            var store = _storeService.GetStoreById(currentProduct.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }

            var gridModel = new GridModel();
            IList<int> filterableSpecificationAttributeOptionIds = null;
            var products = _productService.SearchProducts(0, model.SearchCategoryId,
                model.SearchManufacturerId, null, null, null, 0, model.SearchProductName, false, false,
                _workContext.WorkingLanguage.Id, new List<int>(),
                ProductSortingEnum.Position, command.Page - 1, command.PageSize,
                false, out filterableSpecificationAttributeOptionIds, true, Store.AllStoreReservedId);
            gridModel.Data = products.Select(x => x.ToModel());
            gridModel.Total = products.TotalCount;
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [HttpPost]
        [FormValueRequired("save")]
        public ActionResult CrossSellProductAddPopup(string btnId, string formId, ProductModel.AddCrossSellProductModel model)
        {
            var customer = _workContext.CurrentCustomer;
            bool canManageAllStore = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog) && customer.StoreId == Store.AllStoreReservedId;
            if (!canManageAllStore)
                return AccessDeniedView();

            if (model.SelectedProductIds != null)
            {
                foreach (int id in model.SelectedProductIds)
                {
                    var product = _productService.GetProductById(id);
                    if (product != null)
                    {
                        var store = _storeService.GetStoreById(product.StoreId);
                        if (store == null)
                        {
                            throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
                        }

                        var existingCrossSellProducts = _productService.GetCrossSellProductsByProductId1(model.ProductId);
                        if (existingCrossSellProducts.FindCrossSellProduct(model.ProductId, id) == null)
                        {
                            _productService.InsertCrossSellProduct(
                                new CrossSellProduct()
                                {
                                    ProductId1 = model.ProductId,
                                    ProductId2 = id,
                                });
                        }
                    }
                }
            }

            ViewBag.RefreshPage = true;
            ViewBag.btnId = btnId;
            ViewBag.formId = formId;
            model.Products = new GridModel<ProductModel>();
            return View(model);
        }

        #endregion

        #region Product pictures

        public ActionResult ProductPictureAdd(int pictureId, int displayOrder, int productId)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var product = _productService.GetProductById(productId);
            if (product == null)
                throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.NoProduct"));
            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return RedirectToAction("List");
            }
            if (!canManageAllStore)
            {
                if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != product.StoreId && customer.Id != store.AdminCustomerId)
                {
                    return AccessDeniedView();
                }
            }

            if (pictureId == 0)
                throw new ArgumentException();

            _productService.InsertProductPicture(new ProductPicture()
            {
                PictureId = pictureId,
                ProductId = productId,
                DisplayOrder = displayOrder,
            });

            _pictureService.SetSeoFilename(pictureId, _pictureService.GetPictureSeName(product.Name));

            return Json(new { Result = true }, JsonRequestBehavior.AllowGet);
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult ProductPictureList(GridCommand command, int productId)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return Content("Access Denied");

            var product = _productService.GetProductById(productId);
            if (product == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.NoProduct"));
            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }
            if (!canManageAllStore)
            {
                if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != product.StoreId && customer.Id != store.AdminCustomerId)
                {
                    return Content("Access Denied");
                }
            }

            var productPictures = _productService.GetProductPicturesByProductId(productId);
            var productPicturesModel = productPictures
                .Select(x =>
                {
                    return new ProductModel.ProductPictureModel()
                    {
                        Id = x.Id,
                        ProductId = x.ProductId,
                        PictureId = x.PictureId,
                        PictureUrl = _pictureService.GetPictureUrl(x.PictureId),
                        DisplayOrder = x.DisplayOrder
                    };
                })
                .ToList();

            var model = new GridModel<ProductModel.ProductPictureModel>
            {
                Data = productPicturesModel,
                Total = productPicturesModel.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductPictureUpdate(ProductModel.ProductPictureModel model, GridCommand command)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return Content("Access Denied");

            var productPicture = _productService.GetProductPictureById(model.Id);
            if (productPicture == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.NoProductPicture"));

            productPicture.DisplayOrder = model.DisplayOrder;
            var product = _productService.GetProductById(productPicture.ProductId);
            if (product == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.NoProduct"));

            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }
            if (!canManageAllStore)
            {
                if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != product.StoreId && customer.Id != store.AdminCustomerId)
                {
                    return Content("Access Denied");
                }
            }

            _productService.UpdateProductPicture(productPicture);

            return ProductPictureList(command, productPicture.ProductId);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductPictureDelete(int id, GridCommand command)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return Content("Access Denied");

            var productPicture = _productService.GetProductPictureById(id);
            if (productPicture == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.NoProductPicture"));

            var productId = productPicture.ProductId;
            var product = _productService.GetProductById(productId);
            if (product == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.NoProduct"));

            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }
            if (!canManageAllStore)
            {
                if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != product.StoreId && customer.Id != store.AdminCustomerId)
                {
                    return Content("Access Denied");
                }
            }

            _productService.DeleteProductPicture(productPicture);

            var picture = _pictureService.GetPictureById(productPicture.PictureId);
            _pictureService.DeletePicture(picture);

            return ProductPictureList(command, productId);
        }

        #endregion

        #region Product specification attributes

        public ActionResult ProductSpecificationAttributeAdd(int specificationAttributeOptionId,
            bool allowFiltering, bool showOnProductPage, int displayOrder, int productId)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var product = _productService.GetProductById(productId);
            if (product == null)
                throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.NoProduct"));

            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }
            if (!canManageAllStore)
            {
                if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != product.StoreId && customer.Id != store.AdminCustomerId)
                {
                    return AccessDeniedView();
                }
            }

            var psa = new ProductSpecificationAttribute()
            {
                SpecificationAttributeOptionId = specificationAttributeOptionId,
                ProductId = productId,
                AllowFiltering = allowFiltering,
                ShowOnProductPage = showOnProductPage,
                DisplayOrder = displayOrder,
            };
            _specificationAttributeService.InsertProductSpecificationAttribute(psa);

            return Json(new { Result = true }, JsonRequestBehavior.AllowGet);
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult ProductSpecAttrList(GridCommand command, int productId)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return Content("Access Denied");

            var product = _productService.GetProductById(productId);
            if (product == null)
                throw new ArgumentException(_localizationService.GetResource("Admin.Product.Common.NoProduct"));

            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }
            if (!canManageAllStore)
            {
                if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != product.StoreId && customer.Id != store.AdminCustomerId)
                {
                    return Content("Access Denied");
                }
            }

            var productrSpecs = _specificationAttributeService.GetProductSpecificationAttributesByProductId(productId);

            var productrSpecsModel = productrSpecs
                .Select(x =>
                {
                    var psaModel = new ProductSpecificationAttributeModel()
                    {
                        Id = x.Id,
                        SpecificationAttributeName = x.SpecificationAttributeOption.SpecificationAttribute.Name,
                        SpecificationAttributeOptionName = x.SpecificationAttributeOption.Name,
                        AllowFiltering = x.AllowFiltering,
                        ShowOnProductPage = x.ShowOnProductPage,
                        DisplayOrder = x.DisplayOrder
                    };
                    return psaModel;
                })
                .ToList();

            var model = new GridModel<ProductSpecificationAttributeModel>
            {
                Data = productrSpecsModel,
                Total = productrSpecsModel.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductSpecAttrUpdate(int psaId, ProductSpecificationAttributeModel model,
            GridCommand command)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return Content("Access Denied");

            var psa = _specificationAttributeService.GetProductSpecificationAttributeById(psaId);
            if (psa == null)
                return Content(_localizationService.GetResource("Admin.Product.ProductSpecAttr.NoSpecificationAttribute"));

            var product = _productService.GetProductById(psa.ProductId);
            if (product == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.NoProduct"));

            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }
            if (!canManageAllStore)
            {
                if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != product.StoreId && customer.Id != store.AdminCustomerId)
                {
                    return Content("Access Denied");
                }
            }

            psa.AllowFiltering = model.AllowFiltering;
            psa.ShowOnProductPage = model.ShowOnProductPage;
            psa.DisplayOrder = model.DisplayOrder;
            _specificationAttributeService.UpdateProductSpecificationAttribute(psa);

            return ProductSpecAttrList(command, psa.ProductId);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductSpecAttrDelete(int psaId, GridCommand command)
        {
            var customer = _workContext.CurrentCustomer;
            bool isStoreAdmin, canManageAllStore, isAccessDenied;
            int curManagedStoreId;
            _permissionService.SecurityAccessForAll(out curManagedStoreId, out isStoreAdmin, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return Content("Access Denied");

            var psa = _specificationAttributeService.GetProductSpecificationAttributeById(psaId);
            if (psa == null)
                return Content(_localizationService.GetResource("Admin.Product.ProductSpecAttr.NoSpecificationAttribute"));

            var product = _productService.GetProductById(psa.ProductId);
            if (product == null)
                return Content(_localizationService.GetResource("Admin.Product.Common.NoProduct"));

            var store = _storeService.GetStoreById(product.StoreId);
            if (store == null)
            {
                return Content(_localizationService.GetResource("Admin.Product.Common.StoreNotExist"));
            }
            if (!canManageAllStore)
            {
                if (curManagedStoreId != Store.AllStoreReservedId && curManagedStoreId != product.StoreId && customer.Id != store.AdminCustomerId)
                {
                    return Content("Access Denied");
                }
            }

            var productId = psa.ProductId;
            _specificationAttributeService.DeleteProductSpecificationAttribute(psa);

            return ProductSpecAttrList(command, productId);
        }

        #endregion

        #region Product tags

        public ActionResult ProductTags()
        {
            return AccessDeniedView();
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult ProductTags(GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            var tags = _productTagService.GetAllProductTags()
                .Select(x =>
                {
                    return new ProductTagModel()
                    {
                        Id = x.Id,
                        Name = x.Name,
                        ProductCount = x.ProductCount
                    };
                })
                .ForCommand(command);

            var model = new GridModel<ProductTagModel>
            {
                Data = tags.PagedForCommand(command),
                Total = tags.Count()
            };
            return new JsonResult
            {
                Data = model
            };
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ProductTagDelete(int id, GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            var tag = _productTagService.GetProductTagById(id);
            if (tag == null)
                throw new ArgumentException("No product tag found with the specified id");
            _productTagService.DeleteProductTag(tag);

            return ProductTags(command);
        }

        //edit
        public ActionResult EditProductTag(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            var productTag = _productTagService.GetProductTagById(id);
            if (productTag == null)
                //No product tag found with the specified id
                return RedirectToAction("List");

            var model = new ProductTagModel()
            {
                Id = productTag.Id,
                Name = productTag.Name,
                ProductCount = productTag.ProductCount
            };
            //locales
            AddLocales(_languageService, model.Locales, (locale, languageId) =>
            {
                locale.Name = productTag.GetLocalized(x => x.Name, languageId, false, false);
            });

            return View(model);
        }

        [HttpPost]
        public ActionResult EditProductTag(string btnId, string formId, ProductTagModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            var productTag = _productTagService.GetProductTagById(model.Id);
            if (productTag == null)
                //No product tag found with the specified id
                return RedirectToAction("List");

            if (ModelState.IsValid)
            {
                productTag.Name = model.Name;
                _productTagService.UpdateProductTag(productTag);
                //locales
                UpdateLocales(productTag, model);

                ViewBag.RefreshPage = true;
                ViewBag.btnId = btnId;
                ViewBag.formId = formId;
                return View(model);
            }

            //If we got this far, something failed, redisplay form
            return View(model);
        }

        #endregion

        #region Export / Import

        public ActionResult DownloadCatalogAsPdf()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            try
            {
                IList<int> filterableSpecificationAttributeOptionIds = null;
                var products = _productService.SearchProducts(0, 0, 0, null, null, null, 0, string.Empty, false, false,
                    _workContext.WorkingLanguage.Id, new List<int>(),
                    ProductSortingEnum.Position, 0, int.MaxValue,
                    false, out filterableSpecificationAttributeOptionIds, true);


                byte[] bytes = null;
                using (var stream = new MemoryStream())
                {
                    _pdfService.PrintProductsToPdf(stream, products, _workContext.CurrentCustomer.Region.Id, _workContext.WorkingLanguage);
                    bytes = stream.ToArray();
                }
                return File(bytes, "application/pdf", "pdfcatalog.pdf");
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }
        }

        public ActionResult ExportXmlAll()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            try
            {
                IList<int> filterableSpecificationAttributeOptionIds = null;
                var products = _productService.SearchProducts(0, 0, 0, null, null, null, 0, string.Empty, false, false,
                    _workContext.WorkingLanguage.Id, new List<int>(),
                    ProductSortingEnum.Position, 0, int.MaxValue,
                    false, out filterableSpecificationAttributeOptionIds, true);

                var xml = _exportManager.ExportProductsToXml(products);
                return new XmlDownloadResult(xml, "products.xml");
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }
        }

        public ActionResult ExportXmlSelected(string selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            var products = new List<Product>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                products.AddRange(_productService.GetProductsByIds(ids, 0));
            }

            var xml = _exportManager.ExportProductsToXml(products);
            return new XmlDownloadResult(xml, "products.xml");
        }

        public ActionResult ExportExcelAll()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            try
            {
                IList<int> filterableSpecificationAttributeOptionIds = null;
                var products = _productService.SearchProducts(0, 0, 0, null, null, null, 0, string.Empty, false, false,
                    _workContext.WorkingLanguage.Id, new List<int>(),
                    ProductSortingEnum.Position, 0, int.MaxValue,
                    false, out filterableSpecificationAttributeOptionIds, true);

                byte[] bytes = null;
                using (var stream = new MemoryStream())
                {
                    _exportManager.ExportProductsToXlsx(stream, products);
                    bytes = stream.ToArray();
                }
                return File(bytes, "text/xls", "products.xlsx");
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }
        }

        public ActionResult ExportExcelSelected(string selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            var products = new List<Product>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                products.AddRange(_productService.GetProductsByIds(ids, 0));
            }

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _exportManager.ExportProductsToXlsx(stream, products);
                bytes = stream.ToArray();
            }
            return File(bytes, "text/xls", "products.xlsx");
        }

        [HttpPost]
        public ActionResult ImportExcel(FormCollection form)
        {
            // Disable the subroutine original import functionality!!
            return AccessDeniedView();

            /*
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            try
            {
                var file = Request.Files["importexcelfile"];
                if (file != null && file.ContentLength > 0)
                {
                    _importManager.ImportProductsFromXlsx(file.InputStream);
                }
                else
                {
                    ErrorNotification(_localizationService.GetResource("Admin.Common.UploadFile"));
                    return RedirectToAction("List");
                }
                SuccessNotification(_localizationService.GetResource("Admin.Catalog.Products.Imported"));
                return RedirectToAction("List");
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }
            */
        }

        #endregion

        #endregion
    }
}
